﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Net;

namespace Miko.CS.Net
{
    public enum MessageType
    {
        None = 0,
        Sucess,
        Error,

        // 服务器为客户端指定的 ID
        ClientID = 8,
        // 客户端向服务器报告自己的信息
        // 或者服务器在广播时的其他用户的信息
        ClientInfo,
        // 服务器向客户端报告所有连接的 ID (广播开始)
        ClientIDs,
        // 广播结束
        BroadcastFinsh,

        // 传输的数据类型
        Stream = 64,
        Text,

        // 文件发送请求 (文件大小 文件名 等信息)
        FileHead = 128,
        FileCompleted,
        // 请求某个文件块
        FileSourceBlock,
        // 发送某个文件块
        FileTargetBlock,
        FileSourceAbort,
        FileTargetAbort,
        FileSourcePause,
        FileTargetPause,
        FileSourceContinue,
        FileTargetContinue,
    }

    public enum FileState
    {
        None = 0,
        Transing,
        Paused,
        Aborted,
        Completed,
    }

    public enum ClientState
    {
        None = 0,
        OnLine,
        Busy,
        Leave,
    }

    public enum MessageRecordType
    {
        None = 0,
        Received,
        Send,
    }

    public enum FileList
    {
        None,
        Source,
        Target,
    }

    public static class Convert
    {
        public static string StateToString(ClientState state)
        {
            switch (state)
            {
                case ClientState.Busy:
                    return "忙碌";
                case ClientState.Leave:
                    return "离开";
                case ClientState.OnLine:
                    return "在线";
                default:
                    return "默认";
            }
        }

        public static Color StateToColor(ClientState state)
        {
            switch (state)
            {
                case ClientState.Busy:
                    return Color.Red;
                case ClientState.Leave:
                    return Color.Gold;
                case ClientState.OnLine:
                    return Color.LawnGreen;
                default:
                    return Color.Gray;
            }
        }

        public static ClientState StringToState(string value)
        {
            switch (value)
            {
                case "忙碌":
                    return ClientState.Busy;
                case "离开":
                    return ClientState.Leave;
                case "在线":
                    return ClientState.OnLine;
                default:
                    return ClientState.None;
            }
        }

        public static string FileStateToString(FileState state)
        {
            switch (state)
            {
                case FileState.Aborted:
                    return "中断";
                case FileState.Completed:
                    return "完成";
                case FileState.None:
                    return "等待";
                case FileState.Paused:
                    return "暂停";
                case FileState.Transing:
                    return "传送";
                default:
                    return string.Empty;
            }
        }

        public static Color FileStateToColor(FileState state)
        {
            switch(state)
            {
                case FileState.Aborted:
                    return Color.Red;
                case FileState.Completed:
                    return Color.Green;
                case FileState.Paused:
                    return Color.Orange;
                case FileState.Transing:
                    return Color.LightBlue;
                default:
                    return Color.Gray;
            }
        }

        public static string SizeToString(long b)
        {
            if (b < 0)
            {
                return string.Empty;
            }
            string[] foramt = { "", "K", "M", "G", "T" };
            long t = b;
            int i = 0;
            while (i < foramt.Length - 1)
            {
                if (t < 1024)
                {
                    break;
                }
                t /= 1024;
                i++;
            }
            return string.Format((b / Math.Pow(1024, i)).ToString("0.00") + " " + foramt[i] + "B");
        }
    }

    public static class StreamHead
    {
        // 每次请求的文件块数 (Ping 高应增加请求数)
        public const int MaxConcurrentSum = 32;
        public const int HeadLength = 32;
        // 每个文件块的大小 (网速慢应减小块大小)
        public const int BlockMaxSize = 32 * 1024;

        const int LengthIndex = 0;
        const int HeadLengthIndex = 4;
        const int ClientIDIndex = 8;
        const int MessageTypeIndex = 12;

        const int ArgsIndex = 16;

        public static void Write(byte[] stream, int length, int clientID, MessageType type)
        {
            Array.Copy(BitConverter.GetBytes(length), 0, stream, LengthIndex, 4);
            Array.Copy(BitConverter.GetBytes(HeadLength), 0, stream, HeadLengthIndex, 4);
            Array.Copy(BitConverter.GetBytes(clientID), 0, stream, ClientIDIndex, 4);
            Array.Copy(BitConverter.GetBytes((int)type), 0, stream, MessageTypeIndex, 4);
        }

        public static void Write(byte[] stream, int length, int clientID, MessageType type, long argla, long arglb)
        {
            Write(stream, length, clientID, type);

            Array.Copy(BitConverter.GetBytes(argla), 0, stream, ArgsIndex, 8);
            Array.Copy(BitConverter.GetBytes(arglb), 0, stream, ArgsIndex + 8, 8);
        }

        public static void Write(byte[] stream, int length, int clientID, MessageType type, int arga, int argb, int argc, int argd)
        {
            Write(stream, length, clientID, type);

            Array.Copy(BitConverter.GetBytes(arga), 0, stream, ArgsIndex, 4);
            Array.Copy(BitConverter.GetBytes(argb), 0, stream, ArgsIndex + 4, 4);
            Array.Copy(BitConverter.GetBytes(argc), 0, stream, ArgsIndex + 8, 4);
            Array.Copy(BitConverter.GetBytes(argd), 0, stream, ArgsIndex + 12, 4);
        }

        public static void Read(byte[] stream, out int length, out int clientID, out MessageType type)
        {
            length = BitConverter.ToInt32(stream, LengthIndex);
            clientID = BitConverter.ToInt32(stream, ClientIDIndex);
            type = (MessageType)BitConverter.ToInt32(stream, MessageTypeIndex);
        }

        public static void ReadExt(byte[] stream, out long argla, out long arglb)
        {
            argla = BitConverter.ToInt64(stream, ArgsIndex);
            arglb = BitConverter.ToInt64(stream, ArgsIndex + 8);
        }

        public static void ReadExt(byte[] stream, out int arga, out int argb, out int argc, out int argd)
        {
            arga = BitConverter.ToInt32(stream, ArgsIndex);
            argb = BitConverter.ToInt32(stream, ArgsIndex + 4);
            argc = BitConverter.ToInt32(stream, ArgsIndex + 8);
            argd = BitConverter.ToInt32(stream, ArgsIndex + 12);
        }
    }

    public class BroadcastStartEventArgs : EventArgs
    {
        public int[] IDs { get; private set; }

        public BroadcastStartEventArgs(int[] ids)
        {
            IDs = ids;
        }
    }

    public class MessageReceivedEventArgs : EventArgs
    {
        public int ClientID { get; private set; }
        public string Message { get; private set; }

        public MessageReceivedEventArgs(int clientID, string message)
        {
            ClientID = clientID;
            Message = message;
        }
    }

    public class MessageRecord
    {
        public MessageRecordType RecordType;
        public int TargetID;
        public string Message;
        public DateTime Time;

        public MessageRecord(int targetID, MessageRecordType type, string message)
        {
            TargetID = targetID;
            RecordType = type;
            Message = message;
            Time = DateTime.Now;
        }
    }

    public class SocketClient
    {
        public ClientInfo Info;
        public Socket Client;
        public Thread Receiver;
        public Thread Sender;
        public List<byte[]> Message;
    }

    public class ClientFile
    {
        public FileState State;
        public int ClientID;
        public long FileID;
        public long Size;
        public long Seek;
        public string FileName;
        public FileStream File;
    }

    public class ClientInfo
    {
        // 这里控制着报头各数据的偏移地址
        // 用于将此类进行序列化和反序化
        const int LengthIndex = 0;
        const int IDIndex = 4;
        const int StateIndex = 8;
        const int NameLengthIndex = 12;
        const int InfoLengthIndex = 16;
        const int ImageLengthIndex = 20;
        const int DataStartIndex = 24;

        const int NameMaxLength = 16;
        const int InfoMaxLength = 256;

        public ClientState State { get; private set; }

        public int ID { get; private set; }
        public string Name { get;set; }
        public string Info { get; private set; }

        public Image Image { get; private set; }

        public ClientInfo()
        {
            State = ClientState.OnLine;
            Name = "anonymous";
            Info = "我的签名";
            Image = null;
        }

        public void Set(ClientInfo info)
        {
            ID = info.ID;
            State = info.State;
            Name = info.Name;
            Info = info.Info;
            Image = info.Image;
        }

        public void Set(int id)
        {
            ID = id;
        }

        public void Set(ClientState state, string name, string info, Image image = null)
        {
            State = state;
            Name = (name.Length > NameMaxLength) ? name.Substring(0, NameMaxLength) : name;
            Info = (info.Length > InfoMaxLength) ? info.Substring(0, InfoMaxLength) : info;
            Image = image;
        }

        // 类的反序化
        public void Set(byte[] stream, int index = 0, int length = 0)
        {
            int dataIndex = index;
            int dataLength;
            dataLength = BitConverter.ToInt32(stream, index + LengthIndex);
            if (length != 0 && dataLength != length)
            {
                Console.WriteLine("ClientInfo 长度不匹配.");
                return;
            }

            int id, state, nameLength, infoLength, imageLength;
            id = BitConverter.ToInt32(stream, index + IDIndex);
            state = BitConverter.ToInt32(stream, index + StateIndex);
            nameLength = BitConverter.ToInt32(stream, index + NameLengthIndex);
            infoLength = BitConverter.ToInt32(stream, index + InfoLengthIndex);
            imageLength = BitConverter.ToInt32(stream, index + ImageLengthIndex);

            ID = id;
            State = (ClientState)state;

            dataIndex += DataStartIndex;
            Name = Encoding.UTF8.GetString(stream, dataIndex, nameLength);

            dataIndex += nameLength;
            Info = Encoding.UTF8.GetString(stream, dataIndex, infoLength);

            if (imageLength > 0)
            {
                dataIndex += infoLength;
                try
                {
                    Image = new Bitmap(new MemoryStream(stream, dataIndex, imageLength));
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    Image = null;
                }
            }
            else
            {
                Image = null;
            }
        }

        // 类的序列化
        public byte[] ToStream()
        {
            int length = DataStartIndex;
            int dataIndex = DataStartIndex;

            byte[] nameStream = Encoding.UTF8.GetBytes(Name);
            length += nameStream.Length;

            byte[] infoStream = Encoding.UTF8.GetBytes(Info);
            length += infoStream.Length;

            byte[] imageStream = null;
            if (Image != null)
            {
                MemoryStream ms = new MemoryStream();
                Image.Save(ms, ImageFormat.Png);
                imageStream = ms.ToArray();
                length += imageStream.Length;
                ms.Dispose();
            }

            byte[] stream = new byte[length];
            Array.Copy(BitConverter.GetBytes(length), 0, stream, LengthIndex, 4);
            Array.Copy(BitConverter.GetBytes(ID), 0, stream, IDIndex, 4);
            Array.Copy(BitConverter.GetBytes((int)State), 0, stream, StateIndex, 4);
            Array.Copy(BitConverter.GetBytes(nameStream.Length), 0, stream, NameLengthIndex, 4);
            Array.Copy(BitConverter.GetBytes(infoStream.Length), 0, stream, InfoLengthIndex, 4);

            Array.Copy(nameStream, 0, stream, dataIndex, nameStream.Length);

            dataIndex += nameStream.Length;
            Array.Copy(infoStream, 0, stream, dataIndex, infoStream.Length);

            if (imageStream != null)
            {
                dataIndex += infoStream.Length;
                Array.Copy(BitConverter.GetBytes(imageStream.Length), 0, stream, ImageLengthIndex, 4);
                Array.Copy(imageStream, 0, stream, dataIndex, imageStream.Length);
            }

            return stream;
        }
    }
}
